﻿#include "db_api_.h"

static int add_record(int id, AUDIORECORD* rec)
{
	int res = 0;
	unsigned int br;
	res += f_lseek(&hDB->fdb, sizeof(AUDIORECORD)*(long long)id);
	res += f_write(&hDB->fdb, rec, sizeof(AUDIORECORD), &br);
	return res;
}

int get_audio_info(AUDIORECORD* rec, int media_type)
{
	int res = 0;
	ID3_INFO info = { 0 };
	int ret = -1;

	switch (media_type & 0x0000ffff)
	{
	case MEDIA_LO_MP3:
		ret = db_mp3_parse(rec->path, &info);
		break;

	case MEDIA_LO_WMA:
		ret = db_wma_parse(rec->path, &info);
		break;

	case MEDIA_LO_OGG:
		ret = db_ogg_parse(rec->path, &info);
		break;

	case MEDIA_LO_FLAC:
		ret = db_flac_parse(rec->path, &info);
		break;

	case MEDIA_LO_APE:
		ret = db_ape_parse(rec->path, &info);
		break;

	case MEDIA_LO_AAC:
	case MEDIA_LO_M4A:
	case MEDIA_LO_WAV:
	case MEDIA_LO_AVI:
		break;

	default:
		assert(0);
		break;
	}

	if (ret == 0)
	{
		res += id3_to_utf8(&info.album, (unsigned char*)rec->album, MAXID3ITEM - 1);
		res += id3_to_utf8(&info.artist, (unsigned char*)rec->artist, MAXID3ITEM - 1);
	}

	return res;
}

static int parse(const char* path, const char* fname)
{
	int res = 0;
	AUDIORECORD rec = { 0 };
	int media_type = file_type(path);
	if ((media_type & MEDIA_HI_AUDIO) == 0)return 0;

	strcpy(rec.path, path);

	name_to_utf8((const unsigned char*)fname, (unsigned char*)rec.fname, MAXUTF8NAME);

	res += get_audio_info(&rec, media_type);	if (res)return res;
	if (rec.album[0] == 0)
		strcpy(rec.album, "unknow");
	if (rec.artist[0] == 0)
		strcpy(rec.artist, "unknow");
	
	return  add_record(hDB->audio_count++, &rec);
}

static int scan_file(char* root)
{
	int res = 0;
	DIR dj;         /* Directory object */
	FILINFO fno;    /* File information */

	res += f_findfirst(&dj, &fno, root, "*.*");	if (res)return res;

	while (res == FR_OK && fno.fname[0])
	{
		if ((fno.fattrib & (AM_ARC | AM_DIR | AM_HID | AM_SYS)) == AM_ARC)
		{
			char path[MAXPATH];
			int tmp_len = strlen(root) + strlen("\\") + strlen(fno.fname);
   			if (tmp_len < MAXPATH)
			{
				strcpy(path, root);
				strcat(path, "\\");
				strcat(path, fno.fname);

				res += parse(path, fno.fname);	if (res)break;
				if (hDB->audio_count >= MAXRECORD) break;
			}
		}

		hDB->cb(-555, (hDB->percent++/50)%100);
		res += f_findnext(&dj, &fno);			if (res)break;
		DB_SLP
	}

	res += f_closedir(&dj);
	return res;
}

static int scanfile(char* root, int* nest, const char * ignore_dir[])
{
	int res = 0;
	DIR dj;         /* Directory object */
	FILINFO fno;    /* File information */

	if ((*nest) > NEST)return res;
	if (hDB->audio_count >= MAXRECORD)return res;

	res += scan_file(root);

	res += f_findfirst(&dj, &fno, root, "*");	if (res)return 0;

	while (res == FR_OK && fno.fname[0])
	{
		if ((fno.fattrib & AM_DIR)
			&& (0 != strcmp(fno.fname, "."))
			&& (0 != strcmp(fno.fname, "..")))
		{
			char path[MAXPATH];
			int tmp_len = strlen(root) + strlen("\\") + strlen(fno.fname);
   			if (tmp_len < MAXPATH)
			{
				strcpy(path, root);
				strcat(path, "\\");
				strcat(path, fno.fname);
	
				if (0 == is_ignore_dir(path) && 0 == is_ignore_dirA(path, ignore_dir))
				{
					(*nest)++;
					res += scanfile(path, nest, ignore_dir);	if (res)break;
					(*nest)--;
					if (hDB->audio_count >= MAXRECORD)	break;
				}
			}
		}
		res += f_findnext(&dj, &fno);			if (res)break;
		DB_SLP
	}

	res += f_closedir(&dj);

	return res;
}

#if SORT == 1
static void get_sortid(char* path, unsigned long long id[2])
{
	int i;
	int len;
	char name[MAXPATH] = {0};
	split_path(path, NULL, NULL, name, NULL);
	len = strlen(name);
	id[0] = id[1] = 0;

	for(i=0; i<8 && i<len; i++)
	{
		unsigned long long c = (unsigned long long)(unsigned char)(name[i]);
		id[0] |= c<<((7-i)*8);
	}
	for(i=8; i<16 && i<len; i++)
	{
		unsigned long long c = (unsigned long long)(unsigned char)(name[i]);
		id[1] |= c<<((15-i)*8);
	}
}

static int load_sort(SORTNODE *tmp)
{
	unsigned int i;
	FIL f;
	int res = 0;
	unsigned int br;
	AUDIORECORD rec;

	res += f_open(&f, (const char*)(F_DBAUDIO), (unsigned char)(FA_READ));
	if(res) return -1;

	for (i = 0; i < hDB->audio_count; i++)
	{
		tmp[i].offset = (int)f_tell(&f);
		res += f_read(&f, &rec, sizeof(AUDIORECORD), &br);
		if (FR_OK == res && sizeof(AUDIORECORD) == br)
		{
			get_sortid(rec.path, tmp[i].id);
		}
	}

	res += f_close(&f);
	return res;
}

static int save_sort(SORTNODE *tmp)
{
	unsigned int i;
	int res = 0;
	unsigned int br;
	AUDIORECORD rec;
	FIL f_src, f_des;

	res += f_open(&f_src, (const char*)(F_DBAUDIO), (unsigned char)(FA_READ));
	if(res) return -1;
	res += f_open(&f_des, (const char*)(F_SORT), (unsigned char)(FA_READ | FA_WRITE | FA_CREATE_ALWAYS));
	if(res) return -1;

	for(i=0; i<hDB->audio_count; i++)
	{
		res += f_lseek(&f_src, tmp[i].offset);
		res += f_read(&f_src, &rec, sizeof(AUDIORECORD), &br);
		res += f_write(&f_des, &rec, sizeof(AUDIORECORD), &br);
	}

	res += f_close(&f_src);
	res += f_close(&f_des);

	res += f_unlink((const char*)(F_DBAUDIO));
	res += f_rename((const char*)(F_SORT), (const char*)(F_DBAUDIO));

	return res;
}

static int sort(void)
{
	int res = 0;
	SORTNODE *tmp = 0;

	assert(MAXRECORD >= hDB->audio_count);
	if(hDB->audio_count < 2) return 0;

	tmp = (SORTNODE*)OAL_malloc(sizeof(SORTNODE) * hDB->audio_count);
	if (tmp == 0)
	{
		hDB->cb(-888, 0);
		return -1;
	}

	res += load_sort(tmp);
	if (res == FR_OK)
	{
		_db_sort(tmp, hDB->audio_count);
		res += save_sort(tmp);
	}

	OAL_free(tmp);
	return res;
}
#endif

int audio_build(const char * ignore_dir[])
{
	int res = 0;
	int nest = 0;

	assert(hDB->mount == 1);

	hDB->audio_count = 0;
	hDB->percent = 0;

	res += f_open(&hDB->fdb, (const char*)(F_DBAUDIO), (unsigned char)(FA_READ | FA_WRITE));
	res += scanfile(SDROOT, &nest, ignore_dir);
	hDB->dirty &= ~MODULE_AUDIO;
	hDB->dirty |= MODULE_ALBUM;
	hDB->dirty |= MODULE_ARTIST;
	res += f_close(&hDB->fdb);
#if SORT == 1
	res += sort();
#endif
	hDB->cb(-666, 100);
	return 0;
}

int get_audio_records(unsigned int offset, AUDIORECORD* records, unsigned int cnt)
{
	unsigned int i = 0;
	int res = 0;
	unsigned int br;
	res += f_open(&hDB->fdb, (const char*)(F_DBAUDIO), (unsigned char)(FA_READ | FA_WRITE));
	res += f_lseek(&hDB->fdb, sizeof(AUDIORECORD) * (long long)offset);
	for (i = 0; i < cnt && i + offset < hDB->audio_count; i++)
	{
		res += f_read(&hDB->fdb, &records[i], sizeof(AUDIORECORD), &br);
	}
	res += f_close(&hDB->fdb);
	return res ? -1 : i;
}

int get_audio_name(unsigned int offset, RECUTF8NAME* fname, unsigned int cnt)
{
	unsigned int i = 0;
	int res = 0;
	unsigned int br;
	res += f_open(&hDB->fdb, (const char*)(F_DBAUDIO), (unsigned char)(FA_READ | FA_WRITE));
	res += f_lseek(&hDB->fdb, sizeof(AUDIORECORD) * (long long)offset);
	for (i = 0; i < cnt && i + offset < hDB->audio_count; i++)
	{
		AUDIORECORD rec;
		res += f_read(&hDB->fdb, &rec, sizeof(AUDIORECORD), &br);
		memcpy(fname[i], rec.fname, MAXUTF8NAME);
	}
	res += f_close(&hDB->fdb);
	return res ? -1 : i;
}

int load_audio_album(RECID3ITEM *tmp)
{
	unsigned int i;
	FIL f;
	int res = 0;
	unsigned int br;
	AUDIORECORD rec;

	res += f_open(&f, (const char*)(F_DBAUDIO), (unsigned char)(FA_READ));
	if(res) return -1;

	for (i = 0; i < hDB->audio_count; i++)
	{
		res += f_read(&f, &rec, sizeof(AUDIORECORD), &br);
		if (FR_OK == res && sizeof(AUDIORECORD) == br)
		{
			memcpy(tmp[i], rec.album, MAXID3ITEM);
		}
	}

	res += f_close(&f);
	return res;
}

int load_audio_artist(RECID3ITEM *tmp)
{
	unsigned int i;
	FIL f;
	int res = 0;
	unsigned int br;
	AUDIORECORD rec;

	res += f_open(&f, (const char*)(F_DBAUDIO), (unsigned char)(FA_READ));
	if(res) return -1;

	for (i = 0; i < hDB->audio_count; i++)
	{
		res += f_read(&f, &rec, sizeof(AUDIORECORD), &br);
		if (FR_OK == res && sizeof(AUDIORECORD) == br)
		{
			memcpy(tmp[i], rec.artist, MAXID3ITEM);
		}
	}

	res += f_close(&f);
	return res;
}

int load_audio_name(int* id, unsigned int cnt, RECUTF8NAME* fname)
{
	unsigned int i = 0;
	FIL f;
	int res = 0;
	unsigned int br;
	AUDIORECORD rec;

	res += f_open(&f, (const char*)(F_DBAUDIO), (unsigned char)(FA_READ));
	if(res) return -1;

	for (i = 0; i < cnt; i++)
	{
		res += f_lseek(&f, id[i]);
		res += f_read(&f, &rec, sizeof(AUDIORECORD), &br);
		memcpy(fname[i], rec.fname, MAXUTF8NAME);
	}

	res += f_close(&f);
	return res;
}

int load_audio_records(int* id, unsigned int cnt, AUDIORECORD* records)
{
	unsigned int i = 0;
	FIL f;
	int res = 0;
	unsigned int br;

	res += f_open(&f, (const char*)(F_DBAUDIO), (unsigned char)(FA_READ));
	if(res) return -1;

	for (i = 0; i < cnt; i++)
	{
		res += f_lseek(&f, id[i]);
		res += f_read(&f, &records[i], sizeof(AUDIORECORD), &br);
	}

	res += f_close(&f);
	return res;
}

#if SORT == 1

int delete_audio(char* path)
{
	unsigned int i = 0;
	int res = 0;
	unsigned int br;
	AUDIORECORD rec;
	FIL f;

	res += f_open(&hDB->fdb, (const char*)(F_DBAUDIO), (unsigned char)(FA_READ));
	res += f_open(&f, (const char*)(F_SORT), (unsigned char)(FA_READ | FA_WRITE | FA_CREATE_ALWAYS));

	for (i = 0; i < hDB->audio_count; i++)
	{
		res += f_read(&hDB->fdb, &rec, sizeof(AUDIORECORD), &br);
		if (dircmp(path, rec.path))
		{
			res += f_write(&f, &rec, sizeof(AUDIORECORD), &br);
		}
		else
		{
			for (++i; i < hDB->audio_count; i++)
			{
				res += f_read(&hDB->fdb, &rec, sizeof(AUDIORECORD), &br);
				res += f_write(&f, &rec, sizeof(AUDIORECORD), &br);
			}
			hDB->audio_count--;
			break;
		}
	}

	res += f_close(&hDB->fdb);
	res += f_close(&f);

	res += f_unlink((const char*)(F_DBAUDIO));
	res += f_rename((const char*)(F_SORT), (const char*)(F_DBAUDIO));

	return res;
}

#else

int delete_audio(char* path)
{
	unsigned int i = 0;
	int res = 0;
	unsigned int br;
	AUDIORECORD rec;

	res += f_open(&hDB->fdb, (const char*)(F_DBAUDIO), (unsigned char)(FA_READ | FA_WRITE));

	for (i = 0; i < hDB->audio_count; i++)
	{
		res += f_read(&hDB->fdb, &rec, sizeof(AUDIORECORD), &br);
		if (dircmp(path, rec.path) == 0)
		{
			long long ofs = f_tell(&hDB->fdb) - sizeof(AUDIORECORD);
			hDB->audio_count--;
			res += f_lseek(&hDB->fdb, sizeof(AUDIORECORD) * (hDB->audio_count));
			res += f_read(&hDB->fdb, &rec, sizeof(AUDIORECORD), &br);
			res += f_lseek(&hDB->fdb, ofs);
			res += f_write(&hDB->fdb, &rec, sizeof(AUDIORECORD), &br);
			break;
		}
	}

	res += f_close(&hDB->fdb);
	return res;
}

#endif
